// 作图基本参数
var svg_w, svg_h, padding, ws, fsize, capR, limit, unit, ref_pos_unit, colors;

// 选项卡
d3.selectAll('.tabbar a').on('click', function (event) {
    event.preventDefault();
    var a1 = d3.select(this);
    var did = a1.attr('href').replace('#', '');
    tabs_show(did);
});

// 启动时
tabs_show('chr_info');
show_cname_and_length();
d3.selectAll('#phy_locate input[type="number"]')
    .classed('num-text', true);
d3.selectAll('#img_settings input[type="number"]')
    .classed('num-text', true);
d3.selectAll('input[data-jscolor="{}"]')
    .classed('num-text', true);
d3.select('#result').style('min-height', '1000px');

// 染色体名称及长度
d3.select('#species').on('change', show_cname_and_length);

// 使用示例数据
d3.selectAll('a.example').on('click', function (event) {
    event.preventDefault();
    var a1 = d3.select(this),
        fname = a1.attr('data-fname'),
        idFor = a1.attr('data-for');
    d3.text('/src/pp_locator/data/' + fname)
        .then(function (data) {
            d3.select('#' + idFor).property('value', data);
        });
});

// save SVG
d3.select('#save-svg').on('click', function () {
    saveSvgByPID('img-svg');
});
// 画图
d3.select('#plot').on('click', function () {
    if (!input_data_ok()) return; // 输入有问题，直接结束
    d3.select('#img-svg').html(''); // 清空

    basci_params(); // 更新基本作图参数

    // 给中间的 link 一个保底值
    var minWidth4link = 20;
    var width = svg_w - padding.left - padding.right,
        height = svg_h - padding.top - padding.bottom,
        w4eachChr = ws.pos + (ws.gap + ws.branch) * 2 + ws.chr + ws.sid;

    var d4chrLen = getData4chrLen();
    if (Object.keys(d4chrLen).length === 0) return err_warn('没获得染色体长度数据');

    var chrs = dspv('chr-group').split('\n')[0].trim().split(/\s+/);
    chrs = chrs.filter(d => d4chrLen[d]); // 只取有效的
    if (chrs.length === 0) return err_warn('没指定有效染色体！');

    // 保底宽度
    var minWidth = w4eachChr * chrs.length + minWidth4link * (chrs.length - 1);
    var w4eachLink = minWidth4link;
    if (minWidth > width) {
        svg_w += minWidth - width;
        d3.select('#svg-w').property('value', svg_w); // 更新总宽度
        width = minWidth;
    } else {
        w4eachLink = chrs.length > 1 ? (width - w4eachChr * chrs.length) / (chrs.length - 1) : 0;
    }

    var svg = d3.select('#img-svg').append('svg')
        .attr('width', svg_w).attr('height', svg_h)
        .attr('xmlns', 'http://www.w3.org/2000/svg')
        .attr('version', '1.1'),
        g = svg.append('g').attr('class', 'container')
            .attr('transform', 'translate(' + padding.left + ',' + padding.top + ')');

    var minS = d3.min(chrs.map(d => d4chrLen[d].s));
    var maxE = d3.max(chrs.map(d => d4chrLen[d].e));
    unit = (maxE / 1e6 >= 1) ? 1e6 : ((maxE / 1e3 >= 1) ? 1e3 : 1);
    err_warn("刻度单位为：" + d3.format('.0s')(unit) + 'bp', true);
    var yScale = d3.scaleLinear().range([0, height]).domain([minS, maxE]),
        color_change = d3.interpolateRgb(colors.lower, colors.upper); // 先使用白变红

    var gChr = g.selectAll('g.chr').data(chrs).enter().append('g')
        .attr('class', 'chr')
        .attr('transform', function (d, i) {
            return 'translate(' + (i * w4eachChr + i * w4eachLink) + ',0)';
        });

    // 主体
    gChr.append('rect').attr('class', 'main-chr')
        .attr('x', ws.pos + ws.gap + ws.branch)
        .attr('y', function (d) { return yScale(d4chrLen[d].s) - capR; })
        .attr('width', ws.chr)
        .attr('height', function (d) {
            return yScale(d4chrLen[d].e + 1) - yScale(d4chrLen[d].s) + capR * 2;
        })
        .attr('rx', d => capR).attr('ry', d => capR)
        .attr('stroke', colors.chr_rect).attr('stroke-width', 1)
        .attr('fill', colors.lower)
        ;
    // chr name
    gChr.append('text').attr('class', 'chr-name')
        .attr('x', ws.pos + ws.gap + ws.branch + ws.chr / 2).attr('y', -20)
        .attr('font-size', fsize.cname + 'px')
        .attr('text-anchor', 'middle')
        .attr('fill', colors.cname)
        .text(function (d) { return d; });

    // 参考序列分布，如 CCS1
    var d4refSeq = getData4refSeq();
    if (d4refSeq) {
        gChr.append('g').attr('class', 'ref-seq')
            .attr('stroke-width', yScale(ref_pos_unit) - yScale(0)) // 注意单位, 2021.07.26
            .selectAll('line')
            .data(cname => d4refSeq.filter(v => v.cname === cname && v.bp >= d4chrLen[cname].s && v.bp <= d4chrLen[cname].e))
            .enter().append('line')
            .attr('x1', ws.pos + ws.gap + ws.branch + 1)
            .attr('y1', function (d) { return yScale(d.bp + 0.5 * ref_pos_unit); })
            .attr('x2', ws.pos + ws.gap + ws.branch + ws.chr - 1)
            .attr('y2', function (d) { return yScale(d.bp + 0.5 * ref_pos_unit); })
            .attr('stroke', function (d) { return d.cnt > limit.upper ? color_change(1) : color_change(d.cnt / limit.upper); })
            ;
    }

    var d4phy = getPhy(chrs, yScale);

    chrs.forEach(cname => {
        d4phy[cname] = arrange_y(d4phy[cname], +fsize.label + 5, yScale);
    });
    // 枝杈
    var gMarker = gChr.selectAll('g.marker').data(cname => d4phy[cname])
        .enter().append('g').attr('class', 'marker');
    gMarker.append('line')
        .attr('x1', ws.pos + ws.gap)
        .attr('y1', function (d) { return d.y; })
        .attr('x2', ws.pos + ws.gap + ws.branch)
        .attr('y2', function (d) { return d.y_raw; })
        .attr('stroke', colors.branch).attr('stroke-width', 1)
        ;
    gMarker.append('line')
        .attr('x1', ws.pos + ws.gap + ws.branch + ws.chr)
        .attr('y1', function (d) { return d.y_raw; })
        .attr('x2', ws.pos + ws.gap + ws.branch + ws.chr + ws.branch)
        .attr('y2', function (d) { return d.y; })
        .attr('stroke', colors.branch).attr('stroke-width', 1)
        ;
    // 染色体上连接线 -- 可选
    if (d3.select('#branch-link').property('checked')) {
        gMarker.append('line')
            .attr('x1', ws.pos + ws.gap + ws.branch)
            .attr('y1', function (d) { return d.y_raw; })
            .attr('x2', ws.pos + ws.gap + ws.branch + ws.chr)
            .attr('y2', function (d) { return d.y_raw; })
            .attr('stroke', colors.branch).attr('stroke-width', 1)
            ;
    }
    // pos
    gMarker.append('text')
        .attr('x', ws.pos)
        .attr('y', function (d) { return d.y; })
        .attr('text-anchor', 'end')
        .attr('fill', function (d) { return d.color; })
        .text(function (d) {
            return d3.format('.2f')(d.sstart / unit); // 单位, 2021.07.26
        });

    // id
    gMarker.append('text')
        .attr('x', ws.pos + (ws.gap + ws.branch) * 2 + ws.chr)
        .attr('y', function (d) { return d.y; })
        .attr('fill', function (d) { return d.color; })
        .text(function (d) {
            return d.qseqid;
        });

    gMarker.selectAll('text')
        .attr('dy', 0.32 * fsize.label)
        .attr('font-size', fsize.label + 'px')
        ;

    // link
    var gLink = g.append('g').attr('class', 'links')
        .attr('stroke', colors.link).attr('stroke-width', 1)
        .attr('stroke-opacity', 0.7)
        ;
    var yTop_jitter = d3.randomUniform(-50, -90),
        yBottom_jitter = d3.randomUniform(height + 10, height + 50);

    chrs.forEach((cname, i) => {
        if (i === chrs.length - 1) return; // 最后一个
        d4phy[cname].forEach(di => {
            for (var j = i + 1; j < chrs.length; j++) {
                var both = d4phy[chrs[j]].filter(dj => di.qseqid === dj.qseqid);
                if (both.length > 0) {
                    if (j == i + 1) {
                        gLink.append('line')
                            .attr('x1', (i + 1) * w4eachChr + i * w4eachLink + ws.gap)
                            .attr('y1', di.y)
                            .attr('x2', j * (w4eachChr + w4eachLink) - ws.gap)
                            .attr('y2', both[0].y);
                    } else {
                        // 看看上边近还是下面近
                        var ty = (di.y + both[0].y <= 2 * height - di.y - both[0].y) ? yTop_jitter() : yBottom_jitter(); // tmp y
                        gLink.append('line')
                            .attr('x1', (i + 1) * w4eachChr + i * w4eachLink + ws.gap)
                            .attr('y1', di.y)
                            .attr('x2', (i + 1) * (w4eachChr + w4eachLink) - ws.gap)
                            .attr('y2', ty);
                        gLink.append('line')
                            .attr('x1', (i + 1) * (w4eachChr + w4eachLink) - ws.gap)
                            .attr('y1', ty)
                            .attr('x2', j * (w4eachChr + w4eachLink) - w4eachLink + ws.gap)
                            .attr('y2', ty);
                        gLink.append('line')
                            .attr('x1', j * (w4eachChr + w4eachLink) - w4eachLink + ws.gap)
                            .attr('y1', ty)
                            .attr('x2', j * (w4eachChr + w4eachLink) - ws.gap)
                            .attr('y2', both[0].y);
                    }

                    di.linked2right = 1;
                    return;
                }
            }
        });
    });

    // 添加一个 y 坐标
    if (d3.select('#ruler').property('checked')) {
        var gY = g.append('g').attr('class', 'y-axis')
            .attr("transform", "translate(" + (-ws.gap) + ",0)");
        gY.call(d3.axisLeft(yScale).ticks(null, 's'));
        gY.selectAll('text')
            .attr('font-size', fsize.tickLabel)
            .attr('dy', 0.32 * fsize.tickLabel)
            .attr('fill', colors.tickLabel);
        gY.selectAll('path').attr('stroke', colors.axis);
        gY.selectAll('line').attr('stroke', colors.axis);
    }

    // 显示按钮
    d3.select('#save-svg').classed('hidden', false);
});

// 获取染色体名称和长度
function show_cname_and_length() {
    d3.select('#cname-len-tab').html('');
    var species = dspv('species');
    if (!species) return;

    d3.json('/pp_locator/chromosome?species=' + species + '&bp=1')
        .then(function (data) {
            var cnames = Object.keys(data),
                txt = '# 以 "#" 开头的是注释行，不是必需的。\n' +
                    '# 第一列为染色体名，第二列是 bp 为单位的长度。\n' +
                    '# 第三列（起始位置）和第四列（终止位置）是可选的，\n' +
                    '# 用来只画染色体的一段。\n' +
                    '# 例如：\n# 1N 123456789 1 20000000\n#\n';
            cnames.forEach(cname => {
                txt += cname + '\t' + data[cname] + '\n';
            });
            d3.select('#cname-len-tab').property('value', txt);
        });
}

// 检查输入数据
function input_data_ok() {
    if (!dspv('cname-len-tab').match(/\S+/)) {
        err_warn('请提供染色体名称及长度信息！');
        return false;
    }
    if (!dspv('chr-group').match(/\S+/)) {
        err_warn('请填写要画到一个组的染色体！');
        return false;
    }
    if (find_neg_num('#phy_locate input[type="number"]') > 0) {
        err_warn('定位数据所在列请勿输入负数！');
        return false;
    }
    if (dspv('ref-data').match(/\S+/)) {
        if (+dspv('ref-pos-unit-1') <= 0) {
            err_warn('参考分布 => 位置（Position）的单位 => 必须为正整数！');
            d3.select('#ref-pos-unit-1').classed('err-num', true);
            return false;
        } else {
            d3.select('#ref-pos-unit-1').classed('err-num', false);
        }
    }
    if (+dspv('upper-limit') < +dspv('lower-limit')) {
        err_warn('上限值不可小于下限值！');
        return false;
    }
    if (find_neg_num('#img_settings input[type="number"]') > 0) {
        err_warn('作图基本参数请勿输入负数！');
        return false;
    }
    err_warn('');
    return true;
}

// 错误或警告信息
function err_warn(txt, is_warn=false) {
    return d3.select('#err-msg').html(txt)
        .classed('emphasis', is_warn ? false : true);
}

// 根据 id 获得 value
function dspv(id) {
    return d3.select('#' + id).property('value');
}

// 获取作图基本参数
function basci_params() {
    svg_w = +dspv('svg-w') || 800;
    svg_h = +dspv('svg-h') || 1000;
    capR = +dspv('cap-radius') || 0;
    padding = {
        top: +dspv('p-top') || 100,
        bottom: +dspv('p-bottom') || 100,
        left: +dspv('p-left') || 60,
        right: +dspv('p-right') || 50
    };
    ws = {
        sid: +dspv('sid-w') || 60,
        pos: +dspv('pos-w') || 40,
        branch: +dspv('branch-w') || 20,
        gap: +dspv('gap-w') || 5,
        chr: +dspv('chr-w') || 10
    };
    fsize = {
        cname: +dspv('fsize-cname') || 16,
        label: +dspv('fsize-label') || 10,
        tickLabel: +dspv('fsize-tickLabel') || 10
    };
    limit = {
        upper: +dspv('upper-limit') || 100,
        lower: +dspv('lower-limit') || 0
    };
    // 页面更新一下
    d3.select('#upper-limit').property('value', limit.upper);
    colors = {
        chr_rect: dspv('chr-color'),
        cname: dspv('cname-color'),
        upper: dspv('e-color'),
        lower: dspv('s-color'),
        label: dspv('label-color'),
        branch: dspv('branch-color'),
        axis: dspv('axis-color'),
        tickLabel: dspv('tickLabel-color'),
        link: dspv('link-color')
    };
}

// 获取染色体长度数据
function getData4chrLen() {
    var txt = dspv('cname-len-tab');
    var data = {};
    txt.split('\n').forEach(line => {
        if (line.match(/^(#|\s)/)) return; // 注释行，或其他不正确的行
        if (line.match(/^\S+\s+\d+/)) {
            var idLen = line.split(/\s+/);
            data[idLen[0]] = {
                len: +idLen[1],
                s: (idLen[2] && +idLen[2] > 0) ? +idLen[2] : 0
            };
            data[idLen[0]].e = (idLen[3] && +idLen[3] > data[idLen[0]].s && +idLen[3] < data[idLen[0]].len) ? +idLen[3] : data[idLen[0]].len;
        }
    });

    return data;
}

// 获取参考分布数据
function getData4refSeq() {
    var txt = dspv('ref-data');
    ref_pos_unit = (+dspv('ref-pos-unit-1')) * (+dspv('ref-pos-unit-2')); // 要检查吗？先不检查吧
    var data = [];
    txt.split('\n').forEach(line => {
        if (line.match(/^(#|\s)/)) return; // 注释行，或其他不正确的行
        if (line.match(/\s/)) {
            var dL = line.split(/\s+/);
            data.push({ cname: dL[0], bp: +dL[1] * ref_pos_unit, cnt: +dL[2] });
        }
    });

    return data;
}

// 获取物理定位数据
function getPhy(chrs, yScale) {
    var txt = dspv('phy-data');
    var columns = {
        qseqid: dspv('col-qseqid'),
        sseqid: dspv('col-sseqid'),
        sstart: dspv('col-sstart'),
        color: +dspv('col-color')
    };
    var data = {};
    var rawData = [];
    txt.split('\n').forEach(line => {
        if (line.match(/^(#|\s)/)) return; // 注释行，或其他不正确的行
        if (line.match(/\t/)) {
            var tmp = line.split(/\t/);
            rawData.push({
                qseqid: tmp[columns.qseqid - 1],
                sseqid: tmp[columns.sseqid - 1],
                sstart: tmp[columns.sstart - 1],
                y_raw: yScale(tmp[columns.sstart - 1]),
                color: columns.color <= 0 ? colors.label : tmp[columns.color - 1]
            });
        }
    });
    chrs.forEach(cname => {
        // 要排序
        data[cname] = rawData.filter(d => d.sseqid === cname)
            .sort(function (a, b) {
                return a.sstart - b.sstart;
            });
    });

    return data;
}

// 看是否填入了负数
function find_neg_num(selector) {
    var num_neg = 0; // 负数
    d3.selectAll(selector)
        .select(function () {
            var s1 = d3.select(this);
            if (+s1.property('value') < 0) {
                num_neg += 1;
                s1.classed('err-num', true);
            } else {
                s1.classed('err-num', false);
            }
        });
    return num_neg;
}